package com.xt.base.dao.hibernate;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.xt.base.dao.hibernate.code.CodeGenerator;
import com.xt.base.dao.hibernate.code.IAutoCreateCode;

/**
 * Hibernate Entity Dao基类. </p>
 * 
 * <p>
 * <a href="BaseDao.java.html"><i>查看源文件</i></a>
 * </p>
 * 
 * @author 杨洪波
 */
public abstract class BaseDaoHibernate extends HibernateDaoSupport implements Serializable {
	
	private static final long serialVersionUID = 3408623216458539636L;

	private final static int EXPORT = -1;

	protected final Log log = LogFactory.getLog(getClass());

	public void save(Object o) {
		if(o instanceof IAutoCreateCode){
			Set<IAutoCreateCode> iacSet = ((IAutoCreateCode)o).getTargets();
			Session session = this.getSession();
			for(IAutoCreateCode iac:iacSet)
				CodeGenerator.setCode(iac,session);
		}
		getHibernateTemplate().saveOrUpdate(o);
	}
	public void saveObject(Object o) {
		save(o);
	}

	@SuppressWarnings("unchecked")
	public void saveAll(Collection o) {
		getHibernateTemplate().saveOrUpdateAll(o);
	}

	@SuppressWarnings("unchecked")
	public void removeById(Class clazz,Serializable id) {
		remove(this.getObject(clazz, id));
	}

	public void remove(Object o) {
		getHibernateTemplate().delete(o);
	}

	public Page findBy(DetachedCriteria detachedCriteria, int pageNumber, int pageSize) {
		Criteria c = detachedCriteria.getExecutableCriteria(getSession());
		return findBy(c, pageNumber, pageSize);
	}

	public Criteria getCriteria(DetachedCriteria detachedCriteria) {
		if (detachedCriteria == null)
			return null;
		return detachedCriteria.getExecutableCriteria(getSession());

	}

	/**
	 * 分页查询。
	 * 
	 * @param query
	 *            需要分页的查询
	 * @param pageNumber
	 *            当前页码
	 * @param pageSize
	 *            每页显示记录数。
	 */
	@SuppressWarnings("unchecked")
	public Page findBy(Query query, int pageNumber, int pageSize) {
		// 设置记录集滚动方式以计算总计条数
		ScrollableResults scrollableResults = query.scroll(ScrollMode.SCROLL_SENSITIVE);
		scrollableResults.last();
		// 总计记录条数
		int totalCount = scrollableResults.getRowNumber();

		if (pageNumber < 1)
			pageNumber = 1;
		List list = null;
		int startIndex = 0;
		if (pageSize == EXPORT) {
			list = query.list();
		} else {
			startIndex = getStartOfAnyPage(pageNumber, pageSize);
			list = query.setFirstResult(startIndex - 1).setMaxResults(pageSize).list();
			// 当前页记录条数
		}
		int avaCount = list == null ? 0 : list.size();
		return new Page(startIndex, avaCount, totalCount + 1, pageSize, list);
	}

	/**
	 * 分页查询。
	 * 
	 * @param criteria
	 *            需要分页的查询
	 * @param pageNumber
	 *            当前页码
	 * @param pageSize
	 *            每页显示记录数。
	 */
	@SuppressWarnings("unchecked")
	public Page findBy(Criteria criteria, int pageNumber, int pageSize) {
		// 总计记录条数
		int totalCount = findByOfTotalRows(criteria);

		if (pageNumber < 1)
			pageNumber = 1;

		List list = null;
		int startIndex = 0;
		// 表示需要导出数据,不需要分页.
		if (pageSize == EXPORT) {
			list = criteria.list();
		} else {
			startIndex = Page.getStartOfAnyPage(pageNumber, pageSize);
			list = criteria.setFirstResult(startIndex - 1).setMaxResults(pageSize).list();
		}
		// 当前页记录条数
		int avaCount = list == null ? 0 : list.size();

		return new Page(startIndex, avaCount, totalCount + 1, pageSize, list);
	}

	/**
	 * 分页查询。
	 * 
	 * @param criteria
	 *            需要分页的查询
	 * @param pageNumber
	 *            当前页码
	 * @param pageSize
	 *            每页显示记录数。
	 */
	@SuppressWarnings("unchecked")
	public Page findPageBy(Criteria criteria, int pageNumber, int pageSize) {
		if (pageNumber < 1)
			pageNumber = 1;
		// 总计记录条数
		int totalCount = findPageByOfTotalRows(criteria);

		List list = null;
		int startIndex = 0;
		// 表示需要导出数据,不需要分页.
		if (pageSize == EXPORT) {
			list = criteria.list();
		} else {
			startIndex = getStartOfAnyPage(pageNumber, pageSize);
			log.debug("***********findBy**************");
			
			//由数据库分页SQL实现
			list = criteria.setFirstResult(startIndex - 1).setMaxResults(pageSize).list();
		}
		// 当前页记录条数
		int avaCount = list == null ? 0 : list.size();

		log.debug("总计记录条数: " + totalCount);

		return new Page(startIndex, avaCount, totalCount + 1, pageSize, list);
	}

	/**
	 * 支持可滚动结果集
	 * 如果数据量大,很影响性能
	 * @param criteria
	 * @return
	 */
	protected int findByOfTotalRows(Criteria criteria) {
		// 设置记录集滚动方式以计算总计条数
		ScrollableResults scrollableResults = criteria.scroll(ScrollMode.SCROLL_SENSITIVE);
		scrollableResults.last();
//		
		// 总计记录条数
		return scrollableResults.getRowNumber();
		
//		return  findPageByOfTotalRows(criteria);
	}

	protected int findByOfTotalRows(Query query) {
		// 设置记录集滚动方式以计算总计条数
		ScrollableResults scrollableResults = query.scroll(ScrollMode.SCROLL_SENSITIVE);
		scrollableResults.last();
		// 总计记录条数
		return scrollableResults.getRowNumber();
	}

	/**
	 * 分页查询时获取总的记录条数
	 * @param criteria
	 * @return
	 */
	protected int findPageByOfTotalRows(Criteria criteria) {
		int totalCount = ((Integer) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();   
		criteria.setProjection(null);
		criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY); 
		return totalCount;
	}

	/**
	 * 获取任一页第一条数据在数据库中的位置
	 */
	public static int getStartOfAnyPage(int pageNo, int pageSize) {
		int startIndex = (pageNo - 1) * pageSize + 1;
		if (startIndex < 1)
			startIndex = 1;
		return startIndex;
	}

	@SuppressWarnings("unchecked")
	public Object getObject(Class clazz, Serializable id) {
		Object o = getHibernateTemplate().get(clazz, id);

		if (o == null) {
			throw new ObjectRetrievalFailureException(clazz, id);
		}

		return o;
	}
	
	@SuppressWarnings("unchecked")
	public <T extends Object> T getT(Class<T> clazz,Serializable id){
		return (T)getObject(clazz,id);
	}

	@SuppressWarnings("unchecked")
	public List getObjects(Class clazz) {
		return getHibernateTemplate().loadAll(clazz);
	}

	@SuppressWarnings("unchecked")
	public void removeObject(Class clazz, Serializable id) {
		getHibernateTemplate().delete(getObject(clazz, id));
	}

	public static Object getPropertyValue(String propertyName, Object bean) {
		BeanWrapper bw = new BeanWrapperImpl(bean);
		Object result = null;
		try {
			result = bw.getPropertyValue(propertyName);
			if (result != null) {
				if (result.getClass().equals(String.class)) {
					if (((String) result).trim().length() == 0)
						return null;
					else
						return result;
				}
			}
			return result;
		} catch (Exception e) {
			return null;
		}

	}

}
